#ifndef ADAPTIVEALGORITHMBASE_H
#define ADAPTIVEALGORITHMBASE_H

#include "SimInterface/IConfigurable.h"
#include "../Polynomial.h"

#include <boost/smart_ptr.hpp>

namespace RafMat
{
namespace SimEngine
{

// Deklaracja zapowiadająca
class AdaptiveAlgorithmBase;
class RSTController;

/**
    Alias na zręczny wskaźnik dla AdaptiveAlgorithmBase.
*/
typedef boost::shared_ptr<AdaptiveAlgorithmBase> AdaptiveAlgorithmBaseSPtr;


/**
  Klasa bazowa algorytmych adaptacyjnych.

  Klasa przystosowana jest do pracy z regulatorem RSTController
  przyjmujący do swej pracy wielomiany R, S i T (i h).
*/
class AdaptiveAlgorithmBase : public SimInterface::IConfigurable
{
public:

    //
    // Aliasy dla skomplikowanych typów:
    //

    /** Alias na wektor liczb zmiennoprzecinkowych */
    typedef std::vector<double> PolyType;

    /** Alias na iterator dla wektora PolyType */
    typedef std::vector<double>::iterator PolyIterator;

    /** Alias na stały iterator dla wektora PolyType */
    typedef std::vector<double>::const_iterator PolyConstIterator;


    AdaptiveAlgorithmBase(RSTController& owner, std::string type,
                          Polynomial r = 1, Polynomial s = 1, Polynomial t = 1, double h = 0);

    AdaptiveAlgorithmBase(RSTController& owner, std::string type, std::string name,
                          Polynomial r = 1, Polynomial s = 1, Polynomial t = 1, double h = 0);


    /**
        Funkcja realizująca jeden krok adaptacji.

        Metoda abstrakcyjna do zaimplementowania przez klasy potomne,
        np. APP, AZP1

        \param [in] ctrlVar - aktualna próbka sterowania u(i)
        \param [in] processVar - aktualna próbka wyjścia y(i)
        \param [in] setPoint - aktualna próbka wartości zadanej w(i)
    */
    virtual void adapt(double ctrlVar, double processVar, double setPoint) = 0;

    virtual void reset() {}

    //Polynomial getR() const;
    //Polynomial getS() const;
    //Polynomial getT() const;

    /**
      Zwraca typ algorytmu.
     */
    std::string getType() const { return m_type;}

    /**
        Zwraca stały współczynnik h dla regulatora.
    */
    double getH() const { return m_h; }

    /**
        Zwraca rząd wielomianu R.
    */
    int getRRank() const { return m_r.degree(); }

    /**
        Zwraca rząd wielomianu S.
    */
    int getSRank() const { return m_s.degree(); }

    /**
        Zwraca rząd wielomianu T.
    */
    int getTRank() const { return m_t.degree(); }

    RSTController& getOwner() { return m_owner; }
    const RSTController& getOwner() const { return m_owner; }

    //
    // Iteratory
    //

    /** Zwraca iterator wskazujący na r0. */
    PolyIterator rbegin() { return m_r.begin(); }

    /** Zwraca iterator wskazujący współczynnik na r_dA. */
    PolyIterator rend() { return m_r.end(); }

    /** Zwraca iterator wskazujący współczynnik na r0 (wersja const). */
    PolyConstIterator rbegin() const { return m_r.begin(); }

    /** Zwraca iterator wskazujący współczynnik na r_dA (wersja const). */
    PolyConstIterator rend() const { return m_r.end(); }

    /** Zwraca iterator wskazujący współczynnik na s0. */
    PolyIterator sbegin() { return m_s.begin(); }

    /** Zwraca iterator wskazujący współczynnik na s_dB. */
    PolyIterator send() { return m_s.end(); }

    /** Zwraca iterator wskazujący współczynnik na s0 (wersja const). */
    PolyConstIterator sbegin() const { return m_s.begin(); }

    /** Zwraca iterator wskazujący na współczynnik s_dB (wersja const). */
    PolyConstIterator send() const { return m_s.end(); }

    /** Zwraca iterator wskazujący współczynnik na t0. */
    PolyIterator tbegin() { return m_t.begin(); }

    /** Zwraca iterator wskazujący współczynnik na t_dC. */
    PolyIterator tend() { return m_t.end(); }

    /** Zwraca iterator wskazujący współczynnik na t0 (wersja const). */
    PolyConstIterator tbegin() const { return m_t.begin(); }

    /** Zwraca iterator wskazujący na współczynnik t_dB (wersja const). */
    PolyConstIterator tend() const { return m_t.end(); }

    //
    // Implementacja IConfigurable
    //

    // Nieimplementowane w klasie bazowej.
    //bool setParameter(const std::string& param, const std::string& value) throw(std::logic_error);

    /**
        Funkcja pobiera parametry regulatora P w postaci łańcuchów znakowych.

        Wspierane parametry:
        - rpoly
        - spoly
        - tpoly
        - h

        Potomne klasy mogą rozszerzać listę wspieranych parametrów.

        \param [in] param Parametr, który chcemy odczytać.
        \return Łańcuch reprezentujący wartość parametru.
        \throws std::logic_error, gdy wykryty zostanie błąd (argumentu lub strumienia danych).
    */
    virtual std::string getParameter(const std::string& param) const throw(std::logic_error);

    virtual SimFile::ConfigWriter& dumpParameters(SimFile::ConfigWriter& o) const;

protected:

    Polynomial m_r;
    Polynomial m_s;
    Polynomial m_t;
    double     m_h;

    // Stałe łańcuchowe na użytek interfejsu IConfigurable.
    static const std::string CONF_RPOLY;
    static const std::string CONF_SPOLY;
    static const std::string CONF_TPOLY;
    static const std::string CONF_H;

private:
    RSTController &m_owner; ///< Regulator będący właścicielem tego obiektu

    std::string m_type;
};

}
}

#endif // AdaptiveAlgorithmBase_H
